package http.mapping;

import http.util.ApplicationContextUtil;
import http.util.ArrayUtil;
import http.util.FullHttpResponseUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Component
public class DefaultHandlerMapping implements HandlerMapping,ApplicationContextAware {
    protected Map<String, HandlerMethod> urlHandlerMap=null;
    private ApplicationContext applicationContext;

    private static String getMapperKey(String uri) {
        String result = uri;
        int indexOf = uri.indexOf("?");
        if (indexOf != -1) {
            result = uri.substring(0, indexOf);
        }
        return result;
    }

    /**
     * 用于映射类下的 index 方法
     * @param uri
     * @return
     */
    private HandlerMethod getHandlerFromUri(String uri){
        HandlerMethod handlerMethod = urlHandlerMap.get(uri);
        if(handlerMethod == null && !uri.endsWith("/")){
            return urlHandlerMap.get(uri+"/");
        }
        return handlerMethod;
    }

    @Override
    public HandlerMethod getHandler(FullHttpRequest req,  ChannelHandlerContext ctx) throws Exception {
        String uri = getMapperKey(req.uri());
        HandlerMethod handler = this.getHandlerFromUri(uri);
        if(handler != null){
            boolean isMethodMatch = this.isMethodMatch(handler, req);
            if(!isMethodMatch){
                FullHttpResponseUtil.response(HttpResponseStatus.METHOD_NOT_ALLOWED,"Method Not Allowed",ctx);
                return null;
            }
            handler.setReq(req);
        }
        return handler;
    }

    private boolean isMethodMatch(HandlerMethod handler ,FullHttpRequest req){
        if(ArrayUtil.isEmpty(handler.getMethodTypes())) {
            return true;
        }
        for (RequestMethod method : handler.getMethodTypes()) {
            if(method.name().equals(req.method().name())){
                return true;
            }
        }
        return false;
    }
    @EventListener(ContextRefreshedEvent.class)
    private void initUrlHandlerMap() {
        urlHandlerMap= new HashMap<>();
        Map<String, Object> controllerMap = applicationContext.getBeansWithAnnotation(Controller.class);
        for (Map.Entry<String, Object> entry : controllerMap.entrySet()) {
            Object controller = entry.getValue();
            RequestMapping classMapping = controller.getClass().getAnnotation(RequestMapping.class);
            String[] urls=null;
            if(classMapping == null){
                urls=new String[0];
            }else{
                urls=new String[classMapping.value().length];
                for (int i = 0; i < classMapping.value().length; i++) {
                    urls[i]=classMapping.value()[i];
                }
            }

            Method[] methods = controller.getClass().getMethods();//拿到所有的public 方法
            for (Method method : methods) {
                RequestMapping mapping = method.getAnnotation(RequestMapping.class);
                if(mapping == null){
                    continue;
                }
                RequestMethod[] methodTypes = mapping.method();
                if(urls.length == 0){
                    for (String m : mapping.value()) {
                         addToUrlHandlerMap(m,new HandlerMethod(controller,method,methodTypes));
                    }
                }else{
                    for (String url : urls) {
                        for (String m : mapping.value()) {
                            addToUrlHandlerMap(getMappingKey(url,m),new HandlerMethod(controller,method,methodTypes));
                        }
                    }
                }
            }
        }
        System.out.println(urlHandlerMap);
    }

    private void addToUrlHandlerMap(String key,HandlerMethod handlerMethod){
        if(!key.startsWith("/")){
            key="/"+key;
        }
        urlHandlerMap.put(key,handlerMethod);
    }


    private String getMappingKey(String classMapper,String methodMapping){
        Assert.hasText(classMapper,"classMapper must not be empty or null");
        Assert.hasText(methodMapping,"methodMapping must not be empty or null");
        String url="";
        if (!classMapper.endsWith("/") && !methodMapping.endsWith("/")) {
            url = classMapper + "/" + methodMapping;
        } else {
            url = classMapper + methodMapping;
        }
        url = url.replace("//", "/");
        return url;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}
